/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Collections.ObjectModel;
using MyPhotoIndex.Interfaces;
using System.IO;
using System.Threading;
using MyPhotoIndex.ImageCache;
using MyPhotoIndex.Utilities;
using System.Windows.Forms;
using System.Net;
using MyPhotoIndex.Plugins.WebCommunity.Properties;
using MyPhotoIndex.Interfaces.Plugins;
using MyPhotoIndex.Interfaces.Imaging;
using MyPhotoIndex.Interfaces.ImageFormatProviders;
using MyPhotoIndex.Controls;
using System.Xml.Serialization;
using System.Xml;
using System.Text;

namespace MyPhotoIndex.Plugins.WebCommunity
{
    public class CommunityMailerPlugin : ISharePlugin
    {
        private IApplicationContext m_applicationContext;
        private int                 m_progressValue;

        class AsyncState
        {
            public int NumberOfFiles;
            public Exception ServiceException;
            public PhotoAttachmentIdResponse Response;
        }

        #region IMailPlugin Members

        public void Send(ReadOnlyCollection<IImageData> imageFiles, Size imageSize)
        {
            #region Get Attachment Id

            m_applicationContext.ProcessProgress.IsInfinite = true;
            m_applicationContext.ProcessProgress.ProcessDescription = this.ToString();
            m_applicationContext.ProcessProgress.Running = true;

            string attachmentId = string.Empty;
            string privateKey = string.Empty;
            string uploadUrl = string.Empty;
            int uploadLimitation = 0;

            try
            {
                AsyncState data = new AsyncState();
                data.NumberOfFiles = imageFiles.Count;
                ThreadPool.QueueUserWorkItem(new WaitCallback(service_GetNewPhotoAttachmentId), data);

                while (data.Response == null &&
                       data.ServiceException == null)
                {
                    Application.DoEvents();
                    Thread.Sleep(100);
                }

                if (data.ServiceException != null)
                {
                    throw data.ServiceException;
                }

                PhotoAttachmentIdResponse response = data.Response;
                attachmentId = response.AttachmentId;
                privateKey = response.PrivateKey;
                uploadUrl = response.UploadUrl;
                uploadLimitation = response.MaxImagesLimitation;

                if (imageFiles.Count > uploadLimitation)
                {
                    ApplicationAlert.Show(Resources.UploadLimitReached + " " + uploadLimitation.ToString(), this.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    m_applicationContext.ProcessProgress.Running = false;
                    return;
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
                ApplicationAlert.Show(Resources.CouldNotConnect, this.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                m_applicationContext.ProcessProgress.Running = false;
                return;
            }

            if (imageFiles.Count > uploadLimitation)
            {
                ApplicationAlert.Show(Resources.UploadLimitReached + " " + uploadLimitation.ToString(), this.ToString(), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                m_applicationContext.ProcessProgress.Running = false;
                return;
            }

            m_applicationContext.ProcessProgress.Running = false;
            #endregion

            UploadData uploadData = new UploadData();
            uploadData.ImageFiles = imageFiles;
            uploadData.ImageSize = imageSize;
            uploadData.AttachmentId = attachmentId;
            uploadData.PrivateKey = privateKey;
            uploadData.UploadUrl = uploadUrl;

            UploadPreviewFiles(uploadData);

            ThreadPool.QueueUserWorkItem(new WaitCallback(SendAsync), uploadData);

            List<int> imageIds = new List<int>();
            foreach (IImageData imageData in uploadData.ImageFiles)
            {
                imageIds.Add(imageData.Id);
            }

            HtmlCopyForm hcf = new HtmlCopyForm(uploadData.AttachmentId, imageIds.ToArray());
            hcf.ShowDialog(ApplicationAlert.DefaultOwner);
        }

        void service_GetNewPhotoAttachmentId(object state)
        {
            AsyncState data = (AsyncState)state;

            try
            {
                HttpWebRequest webRequest = HttpWebRequest.Create("http://upload.piccue.com/preview/upload.ashx") as HttpWebRequest;
                webRequest.Headers.Add("method", "create");
                using (WebResponse response = webRequest.GetResponse())
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(PhotoAttachmentIdResponse));
                        data.Response = serializer.Deserialize(stream) as PhotoAttachmentIdResponse;
                    }
                }
            }
            catch (Exception ex)
            {
                data.ServiceException = ex;
            }
        }

        private void UploadPreviewFiles(UploadData uploadData)
        {
            m_progressValue = 0;
            m_applicationContext.ProcessProgress.IsInfinite = false;
            m_applicationContext.ProcessProgress.ProcessDescription = this.ToString();
            m_applicationContext.ProcessProgress.Maximum = uploadData.ImageFiles.Count;
            m_applicationContext.ProcessProgress.Running = true;

            foreach (IImageData imageData in uploadData.ImageFiles)
            {
                Size newSize = ImageAlgo.GetImageProportionalSize(new SizeF(imageData.Thumbnail.Width, imageData.Thumbnail.Height), new SizeF(130, 100));
                using (Bitmap thumbnail = new Bitmap(imageData.Thumbnail, newSize))
                {
                    UploadOnePreviewImage(uploadData, imageData);
                }
            }

            m_applicationContext.ProcessProgress.Running = false;
        }

        private void UploadOnePreviewImage(UploadData uploadData, IImageData imageData)
        {
            HttpWebRequest webRequest = HttpWebRequest.Create("http://" + uploadData.UploadUrl + "/preview/upload.ashx") as HttpWebRequest;
            RequestBuilder requestBuilder = new RequestBuilder("upload_preview");
            webRequest.Headers.Add("method", "upload_preview");
            webRequest.Headers.Add("attachmentId", uploadData.AttachmentId);
            webRequest.Headers.Add("privateKey", uploadData.PrivateKey);
            webRequest.Headers.Add("fileId", imageData.Id.ToString());

            Image image = imageData.Thumbnail;
            imageData.ThumbnailReferences++;

            using (Stream ms = new MemoryStream(ThumbnailUtilities.ThumbnailToByteArray(image)))
            {
                try
                {
                    requestBuilder.BlobReadProgressEvent += new EventHandler<ProgressEventArgs>(requestBuilder_BlobReadProgressEventSync);
                    requestBuilder.SetBlobParameter("file", ms, "image/jpeg");

                    requestBuilder.InitializeRequest(webRequest);

                    String responseData = String.Empty;
                    WebResponse response = webRequest.GetResponse();

                    using (Stream responseStream = response.GetResponseStream())
                    {
                        //byte[] buffer = new byte[500000];
                        //responseStream.Read(buffer, 0, 500000);
                        //string data = ASCIIEncoding.Default.GetString(buffer);
                    }
                }
                catch (Exception ex)
                {
                    Logger.Log(ex);
                }
            }

            imageData.ThumbnailReferences--;
        }

        public void AddRecipient(System.Net.Mail.MailAddress address)
        {
        }

        public bool RequireRecipients
        {
            get { return false; }
        }

        #endregion

        internal void SendAsync(Object o)
        {
            UploadData uploadData = (UploadData)o;

            if (uploadData.ImageSize.Width <= -1 && uploadData.ImageSize.Height <= -1)
            {
                uploadData.ImageSize = Size.Empty;
            }

            UploadImages(uploadData);
        }

        private void UploadImages(UploadData uploadData)
        {
            m_applicationContext.ProcessProgress.IsInfinite = true;
            m_applicationContext.ProcessProgress.ProcessDescription = this.ToString();
            m_applicationContext.ProcessProgress.Running = true;

            Dictionary<string, string> files = PrepareFilesForUpload(uploadData.ImageFiles, uploadData.ImageSize);

            int totalSize = 0;
            foreach (string file in files.Values)
            {
                FileInfo fi = new FileInfo(file);
                totalSize += (int)fi.Length;
            }

            m_applicationContext.ProcessProgress.Running = false;

            m_progressValue = 0;
            m_applicationContext.ProcessProgress.IsInfinite = false;
            m_applicationContext.ProcessProgress.ProcessDescription = this.ToString();
            m_applicationContext.ProcessProgress.Maximum = totalSize;
            m_applicationContext.ProcessProgress.Running = true;

            foreach (IImageData imageFile in uploadData.ImageFiles)
            {
                int retries = 0;
                while (true)
                {
                    try
                    {
                        string retriesString = (retries > 0) ? "(" +retries.ToString() + " Retries)" : string.Empty;
                        m_applicationContext.ProcessProgress.ProcessDescription = string.Format("{0} - {1}{2}", this.ToString(), Path.GetFileName(imageFile.FileName), retriesString);
                        ProcessOneImageFile(imageFile, uploadData, files);
                        break;
                    }
                    catch (Exception ex)
                    {
                        if (retries == 5)
                        {
                            break;
                        }
                        retries++;
                        Logger.Log(ex);
                    }

                    Thread.Sleep(2000);
                }
            }

            m_applicationContext.ProcessProgress.Running = false;
        }

        private Dictionary<string, string> PrepareFilesForUpload(ReadOnlyCollection<IImageData> imageFiles, Size imageSize)
        {
            Dictionary<string, string> files = new Dictionary<string, string>();
            foreach (IImageData imageData in imageFiles)
            {
                if (imageSize != Size.Empty)
                {
                    string tempFile = Path.GetRandomFileName();
                    tempFile = Path.ChangeExtension(tempFile, "Jpg");
                    tempFile = Path.Combine(Path.GetTempPath(), tempFile);

                    using (Image image = imageData.FormatProvider.LoadImage(imageData, imageSize))
                    {
                        if (image != null)
                        {
                            IImageFormatProvider jpgProvider = ImageFormatProviders.ImageFormatProviders.Default["jpg"];
                            jpgProvider.SaveImage(image, imageData, tempFile);

                            files.Add(imageData.FileName, tempFile);
                        }
                    }
                }
                else
                {
                    files.Add(imageData.FileName, imageData.FileName);
                }
            }

            return files;
        }

        private void ProcessOneImageFile(IImageData imageData, UploadData uploadData, Dictionary<string, string> files)
        {
            int currentProgress = m_progressValue;
            string tempFileName = string.Empty;
            if (files.TryGetValue(imageData.FileName, out tempFileName) == false)
            {
                return;
            }

            HttpWebRequest webRequest = HttpWebRequest.Create("http://" + uploadData.UploadUrl + "/images/upload.ashx") as HttpWebRequest;
            webRequest.Timeout = 500000;
            RequestBuilder requestBuilder = new RequestBuilder("upload_image");
            webRequest.Headers.Add("method", "upload_image");
            webRequest.Headers.Add("attachmentId", uploadData.AttachmentId);
            webRequest.Headers.Add("privateKey", uploadData.PrivateKey);
            webRequest.Headers.Add("fileId", imageData.Id.ToString());

            using (FileStream fs = new FileStream(tempFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                try
                {
                    requestBuilder.BlobReadProgressEvent += new EventHandler<ProgressEventArgs>(requestBuilder_BlobReadProgressEvent);
                    requestBuilder.SetBlobParameter("file", fs, "image/jpeg");

                    requestBuilder.InitializeRequest(webRequest);

                    String responseData = String.Empty;
                    WebResponse response = webRequest.GetResponse();

                    using (Stream responseStream = response.GetResponseStream())
                    {

                    }
                }
                catch (Exception ex)
                {
                    Logger.Log(ex);
                    m_progressValue = currentProgress;
                    throw;
                }
            }

            if (tempFileName.CompareTo(imageData.FileName) != 0)
            {
                File.Delete(tempFileName);
            }

            try
            {
                ImageDataCompiler imageDataCompiler = new ImageDataCompiler(m_applicationContext.ImageTagCollection);
                string infoPackage = imageDataCompiler.CompileOneImageDataPack(imageData);

                webRequest = HttpWebRequest.Create("http://" + uploadData.UploadUrl + "/images/upload.ashx") as HttpWebRequest;
                webRequest.Method = "POST";
                webRequest.Timeout = 20000;
                webRequest.Headers.Add("method", "update_image_metadata");
                webRequest.Headers.Add("attachmentId", uploadData.AttachmentId);
                webRequest.Headers.Add("privateKey", uploadData.PrivateKey);
                webRequest.Headers.Add("fileId", imageData.Id.ToString());

                byte[] data = Encoding.Unicode.GetBytes(infoPackage);
                webRequest.ContentLength = data.Length;
                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    requestStream.Write(data, 0, data.Length);
                    requestStream.Flush();
                }

                webRequest.GetResponse();
               
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }
        }

        void requestBuilder_BlobReadProgressEvent(object sender, ProgressEventArgs e)
        {
            m_progressValue += e.Value;
            m_applicationContext.ProcessProgress.Value = m_progressValue;
        }

        void requestBuilder_BlobReadProgressEventSync(object sender, ProgressEventArgs e)
        {
            m_progressValue += e.Value;
            m_applicationContext.ProcessProgress.Value = m_progressValue;
            Application.DoEvents();
        }

        #region IPlugin Members

        public bool Load(IApplicationContext applicationContext)
        {
            m_applicationContext = applicationContext;
            return true;
        }

        public string Name
        {
            get { return "My Photo Index Web Share"; }
        }

        public void Unload()
        {

        }

        public override string ToString()
        {
            return Name;
        }

        #endregion

        #region IMailPlugin Members

        public Image Icon
        {
            get { return Resources.MPIa ; }
        }

        #endregion

        #region IPlugin Members


        public string Description
        {
            get { return "My Photo Indes web sharing"; }
        }

        #endregion
    }
}
